﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using OfficeOpenXml;
using MyBet.Web.DAL;
using MyBet.Web.DAL.Scores;
using ParieurPro.DAL.DatabaseModel;
using ParieurPro.DAL;

namespace MyBet.Web
{
    public class UpdateScores
    {
        Dictionary<Guid, int> lastRankDictionary = new Dictionary<Guid, int>();
        Dictionary<Guid, int> differentialDictionary = new Dictionary<Guid, int>();

        /// <summary>
        /// Update scores dictionary
        /// </summary>
        /// <param name="currentMatch">Current match to process</param>
        /// <param name="bets">All userbets</param>
        /// <param name="scores">All scores</param>
        private void Update(MyBetDBEntities db, Matchs currentMatch, IList<UserBets> bets, ref Dictionary<Guid, Ranks> scores)
        {
            if (currentMatch == null)
            {
                return;
            }

            var query = from bet in bets
                        where bet.MatchId == currentMatch.MatchId
                        select bet;
            IList<UserBets> betList = query.ToList();
            foreach (UserBets bet in betList)
            {
                Ranks rank = new Ranks();
                UserBets currentBet = bet;
                if (!scores.ContainsKey(bet.UserId))
                {
                    scores.Add(bet.UserId, new Ranks());
                }
                else
                {
                    rank = scores[bet.UserId];
                }
                ClassicScore.ComputeScore(currentMatch, ref currentBet, ref rank);
                BetObject.UpdateBetPoint(db, currentBet);
                scores[bet.UserId] = rank;
            }

        }

        private void AddScores(MyBetDBEntities db, ref Dictionary<Guid, Ranks> total, Dictionary<Guid, Ranks> eventScores, Events evt)
        {
            Dictionary<Guid, Ranks> temporaryDictionary = new Dictionary<Guid, Ranks>();
            long maxScore = 0;

            foreach (Guid userid in eventScores.Keys)
            {
                Ranks rank = eventScores[userid];
                rank.EventCount = 1;

                if (maxScore < rank.TotalScore)
                {
                    maxScore = rank.TotalScore;
                }

                if (total.ContainsKey(userid))
                {
                    total[userid].GoodPredictionCount += rank.GoodPredictionCount;
                    total[userid].GoodScoreCount += rank.GoodScoreCount;
                    total[userid].TotalScore += rank.TotalScore;
                    total[userid].EventCount += 1;
                }
                else
                {
                    total.Add(userid, rank);
                }
            }


            // Order rank by totalscore
            var query = from rnk in total
                        orderby rnk.Value.TotalScore descending, rnk.Value.GoodPredictionCount descending, rnk.Value.GoodScoreCount descending
                        select rnk;
            IList<KeyValuePair<Guid, Ranks>> rankList = query.ToList();

            int position = 1;
            foreach (KeyValuePair<Guid, Ranks> pair in rankList)
            {
                Ranks tmpRank = pair.Value;
                tmpRank.CurrentRank = position;
                temporaryDictionary.Add(pair.Key, tmpRank);
                position++;
            }

            if (eventScores.Count > 0)
            {
                foreach (Guid userid in total.Keys)
                {
                    Ranks rank;
                    byte isBestScore = 0;

                    if (eventScores.ContainsKey(userid))
                    {
                        rank = eventScores[userid];
                        if (rank.TotalScore == maxScore)
                        {
                            isBestScore = 1;
                        }
                    }
                    else
                    {
                        rank = new Ranks();
                    }

                    rank.CurrentRank = temporaryDictionary[userid].CurrentRank;
                    // Store event score in database
                    EventObject.AddOrUpdateEventScore(db, userid, evt, rank, isBestScore);

                    if (lastRankDictionary.ContainsKey(userid))
                    {
                        if (differentialDictionary.ContainsKey(userid))
                        {
                            differentialDictionary[userid] = lastRankDictionary[userid] - rank.CurrentRank;
                        }
                        else
                        {
                            differentialDictionary.Add(userid, lastRankDictionary[userid] - rank.CurrentRank);
                        }
                        lastRankDictionary[userid] = rank.CurrentRank;
                    }
                    else
                    {
                        lastRankDictionary.Add(userid, rank.CurrentRank);
                        differentialDictionary.Add(userid, 0);
                    }
                }
            }
        }

        /// <summary>
        /// Create or update rank
        /// </summary>
        /// <param name="db"></param>
        /// <param name="total"></param>
        private void UpdateRank(MyBetDBEntities db, Dictionary<Guid, Ranks> total)
        {
            List<Ranks> ranks = new List<Ranks>();
            //int position = 1;

            foreach (Guid userId in total.Keys)
            {
                // Get subscription id
                int id = SubscriptionObject.GetSubscriptionId(db, userId);
                Ranks rank = total[userId];
                rank.CurrentRank = lastRankDictionary[userId];
                rank.RankDifferential = null;
                if (differentialDictionary.ContainsKey(userId))
                {
                    rank.RankDifferential = differentialDictionary[userId];
                }
                rank.SubscriptionId = id;
                RankObject.AddOrUpdate(db, rank);
            }

        }

        public void UpdateFromFile(String fileName)
        {
            System.IO.FileInfo fileIn = new System.IO.FileInfo(fileName);
            Dictionary<string, int> teamsId = new Dictionary<string,int>();

            using (ExcelPackage pkg = new ExcelPackage(fileIn))
            {
                // get the first worksheet in the workbook
                ExcelWorksheet worksheet = pkg.Workbook.Worksheets[1];
                // Read first line to get ChampionshipId and seasonId
                int championshipId = Convert.ToInt32(worksheet.Cells[1, 2].Value);
                int seasonId = Convert.ToInt32(worksheet.Cells[1, 4].Value);
                // Current day
                int currentEvent = 0;
                // Current line
                int line = 2;
                // File is finished ?
                bool bContinue = true;
                DateTime dayDate = DateTime.UtcNow;
                DateTime closeDate = DateTime.UtcNow;
                DateTime openDate = DateTime.UtcNow;
                Events evt=null;
                Dictionary<Guid, Ranks> scores = new Dictionary<Guid,Ranks>();

                using (MyBetDBEntities db = new MyBetDBEntities())
                {
                    Dictionary<Guid, Ranks> eventScores = new Dictionary<Guid, Ranks>();
                    IList<UserBets> bets = new List<UserBets>();

                    int? contestId = ContestObject.GetId(db, championshipId, seasonId);
                    if (!contestId.HasValue)
                    {
                        throw new ApplicationException("No contest found");
                    }

                    IList<Guid> users = SubscriptionObject.GetUsersList(db, contestId.Value);
                    foreach (Guid user in users)
                    {
                        scores.Add(user, new Ranks());
                    }

                    // Read match information
                    do
                    {
                        bContinue=(worksheet.Cells[line, 1].Value!=null);
                        if (bContinue)
                        {
                            int dayId = Convert.ToInt32(worksheet.Cells[line, 1].Value);
                            if (currentEvent != dayId)
                            {
                                // Store event score somewhere
                                //this.Invoke(this.ResultDelegate, new object[] { String.Format("Process {0} day", dayId) });
                                
                                if (evt != null)
                                {
                                    if (evt.CloseDate < DateTime.UtcNow)
                                    {
                                        AddScores(db, ref scores, eventScores, evt);
                                    }
                                }
                                eventScores.Clear();
                                dayDate = DateTime.FromOADate(Convert.ToDouble(worksheet.Cells[line, 2].Value));
                                dayDate = new DateTime(dayDate.Year, dayDate.Month, dayDate.Day, 19, 0, 0);
                                TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
                                dayDate = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dayDate, tzi.Id, TimeZoneInfo.Utc.Id);
                                closeDate = new DateTime(dayDate.Year, dayDate.Month, dayDate.Day, dayDate.Hour, dayDate.Minute, dayDate.Second);
                                openDate = closeDate.AddDays(-2);
                                // Verify if day exists in database
                                evt = EventObject.GetEventFromDatabase(db, championshipId, seasonId, dayId);
                                EventObject.AddOrUpdateEvent(db, evt, contestId.Value, dayId, openDate, closeDate);
                                evt = EventObject.GetEventFromDatabase(db, championshipId, seasonId, dayId);
                                currentEvent = dayId;
                                // Get all bets for this event
                                bets = BetObject.GetAllBetsInDatabase(db, championshipId, dayId, seasonId);
                            }
                            
                            string dayHomeTeam = Convert.ToString(worksheet.Cells[line, 3].Value);
                            byte? dayScoreHome = null;
                            byte? dayScoreAway = null;
                            string dayAwayTeam = Convert.ToString(worksheet.Cells[line, 6].Value);
                            int tid = -1;
                            Matchs currentMatch;

                            if (worksheet.Cells[line, 4].Value != null)
                            {
                                dayScoreHome = Convert.ToByte(worksheet.Cells[line, 4].Value);
                            }
                            if (worksheet.Cells[line, 5].Value != null)
                            {
                                dayScoreAway = Convert.ToByte(worksheet.Cells[line, 5].Value);
                            }

                            if (!teamsId.ContainsKey(dayHomeTeam.ToUpper()))
                            {
                                tid = TeamObject.GetTeamId(db, dayHomeTeam.ToUpper());
                                if (tid == -1)
                                {
                                    throw new Exception("Unknown team " + dayHomeTeam);
                                }
                                teamsId.Add(dayHomeTeam.ToUpper(), tid);
                            }
                            if (!teamsId.ContainsKey(dayAwayTeam.ToUpper()))
                            {
                                tid = TeamObject.GetTeamId(db, dayAwayTeam.ToUpper());
                                if (tid == -1)
                                {
                                    throw new Exception("Unknown team " + dayAwayTeam);
                                }
                                teamsId.Add(dayAwayTeam.ToUpper(), tid);
                            }
                            // update match in database

                            currentMatch = MatchObject.AddOrUpdate(db, closeDate, teamsId[dayHomeTeam.ToUpper()], teamsId[dayAwayTeam.ToUpper()], dayScoreHome, dayScoreAway, evt.EventId);

                            if (evt.CloseDate < DateTime.UtcNow)
                            {
                                Update(db, currentMatch, bets, ref eventScores);
                            }
                            else
                            {
                                System.Diagnostics.Trace.WriteLine("not started...");
                            }

                            line = line + 1;
                        }

                    } while (bContinue);

                    if (evt.CloseDate < DateTime.UtcNow)
                    {
                        AddScores(db, ref scores, eventScores, evt);
                    }
                    // Store current rank somewhere
                    UpdateRank(db, scores);
                    db.SaveChanges();
                }

            }
        
        }


    }
}